package com.mo.tools.verification;

/**
 * @name: ObjectUtil
 * @description:
 * @author: Yang Xingfu
 * @date: 2020-09-25 11:11
 **/



import org.apache.xmlbeans.UserType;

import java.lang.reflect.Array;
import java.util.*;

public abstract class ObjectUtil {

    public ObjectUtil() {
    }

    /**
     * Determine the object is an array
     *
     * @param obj object
     * @param <O> generic
     * @return true or false
     */
    public static <O> boolean isArray(O obj) {
        return obj != null && obj.getClass().isArray();
    }

    /**
     * Determine the object is a collection
     *
     * @param obj object
     * @param <C> generic
     * @return true or false
     */
    public static <C> boolean isCollection(C obj) {
        return obj != null && obj instanceof Collection;
    }

    /**
     * Determine the object is a container
     *
     * @param obj object
     * @param <M> generic
     * @return true or false
     */
    public static <M> boolean isContainer(M obj) {
        return obj != null && obj instanceof Map;
    }

    /**
     * Determine the object is empty
     *
     * @param obj object
     * @param <T> generic
     * @return true or false
     */
    public static <T> boolean isEmpty(T obj) {
        if (obj == null) {
            return true;
        } else if (obj instanceof Optional) {
            return !((Optional) obj).isPresent();
        } else if (obj instanceof CharSequence) {
            return ((CharSequence) obj).length() == 0;
        } else if (obj.getClass().isArray()) {
            return Array.getLength(obj) == 0;
        } else if (obj instanceof Collection) {
            return ((Collection) obj).isEmpty();
        } else {
            return obj instanceof Map ? ((Map) obj).isEmpty() : false;
        }
    }


    /**
     * optional to object
     *
     * @param obj optional object
     * @return object
     */
    public static Object optionalToObject(Object obj) {
        if (obj instanceof Optional) {
            Optional<?> optional = (Optional) obj;
            if (!optional.isPresent()) {
                return null;
            } else {
                Object result = optional.get();
                if (result instanceof Optional) {
                    throw new RuntimeException("Multi-level Optional usage not supported");
                }
                return result;
            }
        } else {
            return obj;
        }
    }


    /**
     * array contains element
     *
     * @param array   array
     * @param element element
     * @return true or false
     */
    public static boolean containsElement(Object[] array, Object element) {
        if (array == null || element == null) {
            return false;
        } else if (array == null && array == null) {
            return true;
        } else {
            Object[] var2 = array;
            int var3 = array.length;

            for (int var4 = 0; var4 < var3; ++var4) {
                Object arrayEle = var2[var4];
                if (equals(arrayEle, element)) {
                    return true;
                }
            }

            return false;
        }
    }

    /**
     * Collection contains element
     *
     * @param c   collections
     * @param o   element
     * @param <C> generic
     * @return true or false
     */
    public static <C> boolean containsElement(Collection<C> c, C o) {
        if (c == null || o == null) {
            return false;
        } else if (c == null && o == null) {
            return true;
        } else {
            ArrayList<C> arrayList = new ArrayList<C>(c);
            return arrayList.contains(o);
        }
    }

    /**
     * map contains value
     *
     * @param c   map
     * @param o   element
     * @param <K> key
     * @param <V> value
     * @return true or false
     */
    public static <K, V> boolean containsElement(Map<K, V> c, V o) {
        if (c == null || o == null) {
            return false;
        } else if (c == null && o == null) {
            return true;
        } else {
            Map<K, V> map = new HashMap<K, V>(c);
            return map.containsValue(o);
        }
    }

    /**
     * enum contains element, default caseIgnore
     *
     * @param enumValues enum
     * @param constant   element
     * @return true or false
     */
    public static boolean containsEnum(Enum<?>[] enumValues, String constant) {
        return containsEnum(enumValues, constant, false);
    }


    /**
     * enum contains element
     *
     * @param enumValues    enum
     * @param constant      element
     * @param caseSensitive caseIgnore option
     * @return true or false
     */
    public static boolean containsEnum(Enum<?>[] enumValues, String constant, boolean caseSensitive) {
        Enum[] var3 = enumValues;
        int var4 = enumValues.length;
        int var5 = 0;

        while (true) {
            if (var5 >= var4) {
                return false;
            }

            Enum<?> candidate = var3[var5];
            if (caseSensitive) {
                if (candidate.toString().equals(constant)) {
                    break;
                }
            } else if (candidate.toString().equalsIgnoreCase(constant)) {
                break;
            }

            ++var5;
        }

        return true;
    }

    /**
     * element convert to enum
     *
     * @param enumValues enum
     * @param constant   element
     * @param <E>        generic
     * @return enum upper element
     */
    public static <E extends Enum<?>> E caseInsensitiveToEnum(E[] enumValues, String constant) {
        Enum[] var2 = enumValues;
        int var3 = enumValues.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            E candidate = (E) var2[var4];
            if (candidate.toString().equalsIgnoreCase(constant)) {
                return candidate;
            }
        }

        throw new IllegalArgumentException("Constant [" + constant + "] does not exist in enum type " + enumValues.getClass().getComponentType().getName());
    }

    /**
     * add object to array, return new array
     *
     * @param array array
     * @param obj   object
     * @param <A>   generic
     * @param <O>   generic
     * @return new array
     */
    public static <A, O extends A> A[] addObjectToArray(A[] array, O obj) {
        Class<?> compType = Object.class;
        if (array != null) {
            compType = array.getClass().getComponentType();
        } else if (obj != null) {
            compType = obj.getClass();
        }

        int newArrLength = array != null ? array.length + 1 : 1;
        A[] newArr = (A[]) ((Object[]) Array.newInstance(compType, newArrLength));
        if (array != null) {
            System.arraycopy(array, 0, newArr, 0, array.length);
        }

        newArr[newArr.length - 1] = obj;
        return newArr;
    }

    /**
     * copy array to new array
     *
     * @param source source array
     * @return target array
     */
    public static <O> Object[] CopyObjectArray(O source) {
        if (source instanceof Object[]) {
            return (Object[]) ((Object[]) source);
        } else if (source == null) {
            return null;
        } else if (!source.getClass().isArray()) {
            throw new IllegalArgumentException("Source is not an array: " + source);
        } else {
            int length = Array.getLength(source);
            if (length == 0) {
                return null;
            } else {
                Class<?> wrapperType = Array.get(source, 0).getClass();
                Object[] newArray = (Object[]) ((Object[]) Array.newInstance(wrapperType, length));

                for (int i = 0; i < length; ++i) {
                    newArray[i] = Array.get(source, i);
                }

                return newArray;
            }
        }
    }


    /**
     * object equals object
     *
     * @param o1 object
     * @param o2 object
     * @return true or false
     */
    public static <O> boolean equals(O o1, O o2) {
        if (o1 == o2) {
            return true;
        } else if (o1 != null && o2 != null) {
            if (o1.equals(o2)) {
                return true;
            } else {
                return o1.getClass().isArray() && o2.getClass().isArray() ? arrayEquals(o1, o2) : false;
            }
        } else {
            return false;
        }
    }

    /**
     * array equals array
     *
     * @param o1 array1
     * @param o2 array2
     * @return true or false
     */
    private static <O> boolean arrayEquals(O o1, O o2) {
        if (o1 instanceof Object[] && o2 instanceof Object[]) {
            return Arrays.equals((Object[]) o1, (Object[]) o2);
        } else if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
            return Arrays.equals((boolean[]) o1, (boolean[]) o2);
        } else if (o1 instanceof byte[] && o2 instanceof byte[]) {
            return Arrays.equals((byte[]) o1, (byte[]) o2);
        } else if (o1 instanceof char[] && o2 instanceof char[]) {
            return Arrays.equals((char[]) o1, (char[]) o2);
        } else if (o1 instanceof double[] && o2 instanceof double[]) {
            return Arrays.equals((double[]) o1, (double[]) o2);
        } else if (o1 instanceof float[] && o2 instanceof float[]) {
            return Arrays.equals((float[]) o1, (float[]) o2);
        } else if (o1 instanceof int[] && o2 instanceof int[]) {
            return Arrays.equals((int[]) o1, (int[]) o2);
        } else if (o1 instanceof long[] && o2 instanceof long[]) {
            return Arrays.equals((long[]) o1, (long[]) o2);
        } else {
            return o1 instanceof short[] && o2 instanceof short[] ? Arrays.equals((short[]) o1, (short[]) o2) : false;
        }
    }

}
